﻿using System;
using System.Linq;
using System.Reflection;
using System.Linq.Expressions;
using System.Collections.Generic;
using AutoMapper;

namespace NMA.Application
{
    using NMA.Domain.Model;    
    using NMA.Domain.Model.Repository;
    using NMA.Infrastructure.Logging;    
    using NMA.Infrastructure.EventAggregator;
    using NMA.Infrastructure.NHibernate.Container;
    using NMA.Infrastructure.DBC;
    using NMA.Infrastructure.NHibernate.DomainObject;
    using NMA.Infrastructure.LambdaExpression;
    using NMA.Domain.Model.Service.Application;
    using NMA.Domain.Model.DTO;
    using NMA.Domain.Shared.Paging;
    using NMA.Infrastructure.NHibernate.Paging;    

    public class NewsFacade : INewsFacade
    {
        #region variables

        private readonly INewsRepository _newsRepository = null;
        private readonly IPollRepository _pollRepository = null;
        //private readonly IConsumer<CategoryAuditEvent> _handler = null;
        private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        #endregion

        #region ctors

        public NewsFacade() : this(IoC.GetInstance<INewsRepository>(),
                                   IoC.GetInstance<IPollRepository>()//,
                                   //IoC.GetInstance<IConsumer<CategoryAuditEvent>>()
                                   )               
        {
        }

        public NewsFacade(INewsRepository newsRepository, IPollRepository pollRepository) 
        {
            _newsRepository = newsRepository;
            _pollRepository = pollRepository;
        }

        #endregion

        #region implement functions

        public IEnumerable<NewsDTO> AllNews()
        {
            try
            {
                var result = _newsRepository.All();

                List<NewsDTO> listDto = new List<NewsDTO>();

                foreach (var item in result)
                {
                    listDto.Add(Mapper.Map<News, NewsDTO>((News)item));
                }

                return listDto;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }  
        }

        public IEnumerable<PollDTO> AllPoll()
        {
            try
            {
                var result = _pollRepository.All();

                List<PollDTO> listDto = new List<PollDTO>();

                foreach (var item in result)
                {
                    listDto.Add(Mapper.Map<Poll, PollDTO>((Poll)item));
                }

                return listDto;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public NewsDTO GetNewsById(Guid id)
        {
            Check.Assert(id != null, "Id is null");
            Check.Assert(_newsRepository != null, "News Repository is null");

            try
            {
                INews result = _newsRepository.GetById(id);
                return Mapper.Map<News, NewsDTO>((News)result); ;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }    
        }

        public Guid AddNews(NewsDTO dto)
        {
            Check.Assert(dto != null, "News's object is null");
            Check.Assert(_newsRepository != null, "News Repository is null");

            try
            {
                object result = null;

                var entityMapped = Mapper.Map<NewsDTO, News>(dto);

                result = _newsRepository.Add(entityMapped);

                return (Guid)result;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public void RemoveNews(Guid id)
        {
            Check.Assert(id != null, "News's id is null");
            Check.Assert(_newsRepository != null, "News Repository is null");

            try
            {
                var dto = GetNewsById(id);

                if (dto == null)
                    throw new Exception("News's entity is null");

                RemoveNews(dto);
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public void RemoveNews(NewsDTO dto)
        {
            Check.Assert(dto != null, "News's object is null");
            Check.Assert(_newsRepository != null, "News Repository is null");

            try
            {
                var entityMapped = Mapper.Map<NewsDTO, News>(dto);

                _newsRepository.Remove(entityMapped);
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public void UpdateNews(NewsDTO dto)
        {
            Check.Assert(dto != null, "News's entity is null");
            Check.Assert(_newsRepository != null, "News Repository is null");

            try
            {
                var entityMapped = Mapper.Map<NewsDTO, News>(dto);

                _newsRepository.Update(entityMapped);
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public int CountAllNews()
        {
            Check.Assert(_newsRepository != null, "News Repository is null");

            try
            {
                int result = 0;

                result = _newsRepository.CountAllNews();

                return result;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public int GetNumberOfView(Guid id)
        {
            Check.Assert(id != null, "Id is null");
            Check.Assert(_newsRepository != null, "News Repository is null");

            try
            {
                var entity = GetNewsById(id);

                if (entity == null)
                    throw new Exception("News's object is null");

                return entity.NumberOfView;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public IEnumerable<NewsDTO> GetNewsBy(Expression<Func<NewsDTO, bool>> condition, Type identifyColumn)
        {
            Check.Assert(condition != null, "Condition for searching news is null");
            Check.Assert(identifyColumn != null, "Type is null");
            Check.Assert(_newsRepository != null, "News Repository is null");

            try
            {
                var lambda = LambdaExpressionHelper<INews, NewsDTO>.Convert(condition, identifyColumn);

                var result = _newsRepository.GetBy(lambda);

                IList<NewsDTO> list = new List<NewsDTO>();

                result.ToList().ForEach(x =>
                {
                    list.Add(Mapper.Map<News, NewsDTO>((News)x));
                });

                return list;

            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public int CountAllPoll()
        {
            Check.Assert(_pollRepository != null, "Poll Repository is null");

            try
            {
                int result = 0;

                result = _pollRepository.CountAllPoll();

                return result;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public PollDTO GetPollById(Guid id)
        {
            Check.Assert(_pollRepository != null, "Poll Repository is null");
            Check.Assert(id != null, "Id is null");

            try
            {
                IPoll poll = _pollRepository.GetById(id);

                return Mapper.Map<Poll, PollDTO>((Poll)poll);
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public Guid AddPoll(Guid newsId, PollDTO dto)
        {
            Check.Assert(_newsRepository != null, "News Repository is null");
            Check.Assert(_pollRepository != null, "Poll Repository is null");
            Check.Assert(newsId != null, "News ID is null");
            Check.Assert(dto != null, "Poll's object is null");

            try
            {
                object result = null;

                var entityMapped = Mapper.Map<PollDTO, Poll>(dto);
                result = _pollRepository.Add(entityMapped);
                var newsObj = _newsRepository.GetById(newsId);
                newsObj.Poll = entityMapped;
                _newsRepository.Update(newsObj);

                return (Guid)result;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public void RemovePoll(PollDTO dto)
        {
            Check.Assert(_pollRepository != null, "Poll Repository is null");
            Check.Assert(dto != null, "Poll's object is null");

            try
            {
                var entityMapped = Mapper.Map<PollDTO, Poll>(dto);

                _pollRepository.Remove(entityMapped);
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public void UpdatePoll(PollDTO dto)
        {
            Check.Assert(_pollRepository != null, "Poll Repository is null");
            Check.Assert(dto != null, "Poll's entity is null");

            try
            {
                var entityMapped = Mapper.Map<PollDTO, Poll>(dto);

                _pollRepository.Update(entityMapped);
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public IEnumerable<PollDTO> GetPollBy(Expression<Func<PollDTO, bool>> condition, Type identifyColumn)
        {
            Check.Assert(_pollRepository != null, "Poll Repository is null");
            Check.Assert(condition != null, "Condition for searching poll is null");
            Check.Assert(identifyColumn != null, "Type is null");

            try
            {
                var lambda = LambdaExpressionHelper<IPoll, PollDTO>.Convert(condition, identifyColumn);

                var result = _pollRepository.GetBy(lambda);

                IList<PollDTO> list = new List<PollDTO>();

                result.ToList().ForEach(x =>
                {
                    list.Add(Mapper.Map<Poll, PollDTO>((Poll)x));
                });

                return list;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        public IPagedList<NewsDTO> AllNews(int pageIndex, int pageSize)
        {
            Check.Assert(_newsRepository != null, "News Repository is null");
            Check.Assert(pageIndex > 0, "Page Index is less than zero");
            Check.Assert(pageSize > 0, "Page Size is less than zero");

            var result = _newsRepository.All(pageIndex, pageSize);
            int count = 0;

            IPagedList<NewsDTO> list = new PagedList<NewsDTO>();

            ((IEnumerable<INews>)result).ToList().ForEach(x =>
            {
                list.Add(Mapper.Map<News, NewsDTO>((News)x));
                count++;
            });

            list.TotalCount = count;
            list.PageIndex = pageIndex;
            list.PageSize = pageSize;

            return list;
        }

        public IPagedList<PollDTO> AllPoll(int pageIndex, int pageSize)
        {
            Check.Assert(_pollRepository != null, "Poll Repository is null");
            Check.Assert(pageIndex > 0, "Page Index is less than zero");
            Check.Assert(pageSize > 0, "Page Size is less than zero");

            var result = _newsRepository.All(pageIndex, pageSize);
            int count = 0;

            IPagedList<PollDTO> list = new PagedList<PollDTO>();

            ((IEnumerable<IPoll>)result).ToList().ForEach(x =>
            {
                list.Add(Mapper.Map<Poll, PollDTO>((Poll)x));
                count++;
            });

            list.TotalCount = count;
            list.PageIndex = pageIndex;
            list.PageSize = pageSize;

            return list;
        }

        #endregion
    }
}